﻿using DotNetCommon.Extensions;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using Shouldly;
using System;
using System.Data;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace DotNetCommon.Test.Serialize
{
    [TestFixture]
    internal class DiffNewtonsoftAndSystemTextJson
    {
        class Demo
        {
            public int? Num { get; set; }
        }

        /// <summary>
        /// SystemTextJson 无法 "" => int? 无法转为null
        /// </summary>
        [Test]
        public void Test()
        {
            var str = "{\"Num\":\"\"}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo>(str);
            d.Num.ShouldBe(null);

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.Nullable`1[System.Int32]. Path: $.Num | LineNumber: 0 | BytePositionInLine: 9.");
            }

            //兼容了
            var d2 = str.ToObject<Demo>();
            d2.Num.ShouldBe(null);
        }

        class Demo2
        {
            public int Num { get; set; }
        }
        /// <summary>
        /// 均无法 null 或 "" => int
        /// </summary>
        [Test]
        public void Test2()
        {
            var str = "{\"Num\":null}";
            try
            {
                Newtonsoft.Json.JsonConvert.DeserializeObject<Demo2>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("Error converting value {null} to type 'System.Int32'. Path 'Num', line 1, position 11.");
            }
            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo2>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.Int32. Path: $.Num | LineNumber: 0 | BytePositionInLine: 11.");
            }

            var str2 = "{\"Num\":\"\"}";
            try
            {
                Newtonsoft.Json.JsonConvert.DeserializeObject<Demo2>(str2);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("Error converting value {null} to type 'System.Int32'. Path 'Num', line 1, position 9.");
            }
            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo2>(str2);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.Int32. Path: $.Num | LineNumber: 0 | BytePositionInLine: 9.");
            }
        }

        class Demo3
        {
            public string NumStr { get; set; }
        }

        /// <summary>
        /// SystemTextJson 无法 123 => "123"
        /// </summary>
        [Test]
        public void Test3()
        {
            var str = "{\"NumStr\":123}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo3>(str);
            d.NumStr.ShouldBe("123");

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo3>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.String. Path: $.NumStr | LineNumber: 0 | BytePositionInLine: 13.");
            }

            //兼容了
            var d2 = str.ToObject<Demo3>();
            d.NumStr.ShouldBe("123");
        }

        class Demo4
        {
            public string BoolStr { get; set; }
        }

        /// <summary>
        /// SystemTextJson 无法 true => "true"
        /// </summary>
        [Test]
        public void Test4()
        {
            var str = "{\"BoolStr\":true}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo4>(str);
            d.BoolStr.ShouldBe("true");

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo4>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.String. Path: $.BoolStr | LineNumber: 0 | BytePositionInLine: 15.");
            }

            //兼容了
            var d2 = str.ToObject<Demo4>();
            d2.BoolStr.ShouldBe("true");
        }

        class Demo5
        {
            public bool Bool { get; set; }
        }

        /// <summary>
        /// SystemTextJson 无法 1 => true "true"=>true
        /// </summary>
        [Test]
        public void Test5()
        {
            var str = "{\"Bool\":1}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>(str);
            d.Bool.ShouldBe(true);

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo5>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.Boolean. Path: $.Bool | LineNumber: 0 | BytePositionInLine: 9.");
            }

            //Newtonsoft 0=>false, 其余为true
            str = "{\"Bool\":0}";
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>(str);
            d.Bool.ShouldBe(false);
            str = "{\"Bool\":2}";
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>(str);
            d.Bool.ShouldBe(true);
            str = "{\"Bool\":-1}";
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>(str);
            d.Bool.ShouldBe(true);
            //Newtonsoft "true"=>true "false"=>false 其他报错
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>("{\"Bool\":\"true\"}");
            d.Bool.ShouldBe(true);
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>("{\"Bool\":\"TRUE\"}");
            d.Bool.ShouldBe(true);
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>("{\"Bool\":\"false\"}");
            d.Bool.ShouldBe(false);
            Shouldly.Should.Throw<Exception>(() => Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>("{\"Bool\":\"pl\"}"));
            //Newtonsoft 也无法将 null =>false
            Shouldly.Should.Throw<Exception>(() => Newtonsoft.Json.JsonConvert.DeserializeObject<Demo5>("{\"Bool\":null}"));

            //兼容了
            d = "{\"Bool\":1}".ToObject<Demo5>();
            d.Bool.ShouldBe(true);
            d = "{\"Bool\":0}".ToObject<Demo5>();
            d.Bool.ShouldBe(false);
            d = "{\"Bool\":true}".ToObject<Demo5>();
            d.Bool.ShouldBe(true);
            d = "{\"Bool\":\"true\"}".ToObject<Demo5>();
            d.Bool.ShouldBe(true);
            Shouldly.Should.Throw<Exception>(() => "{\"Bool\":\"pl\"}".ToObject<Demo5>());
            d.Bool.ShouldBe(true);
            Shouldly.Should.Throw<Exception>(() => "{\"Bool\":null}".ToObject<Demo5>());
        }

        class Demo6
        {
            public float Float { get; set; }
        }
        /// <summary>
        /// SystemTextJson 无法 "NaN" => float.NaN
        /// </summary>
        [Test]
        public void Test6()
        {
            var str = "{\"Float\":\"NaN\"}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo6>(str);
            d.Float.ShouldBe(float.NaN);

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo6>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.Single. Path: $.Float | LineNumber: 0 | BytePositionInLine: 14.");
            }

            //Newtonsoft还兼容了大小写
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo6>("{\"Float\":\"nan\"}");
            d.Float.ShouldBe(float.NaN);
            //Newtonsoft还兼容了不带引号的 NaN =>flaot.NaN
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo6>("{\"Float\":NaN}");
            d.Float.ShouldBe(float.NaN);

            //兼容了 "NaN" => flaot.NaN
            d = "{\"Float\":\"NaN\"}".ToObject<Demo6>();
            d.Float.ShouldBe(float.NaN);
            d = "{\"Float\":\"Infinity\"}".ToObject<Demo6>();
            d.Float.ShouldBe(float.PositiveInfinity);

            //但不兼容大小写,如: "nan"
            try
            {
                str = "{\"Float\":\"nan\"}";
                str.ToObject<Demo6>();
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.Single. Path: $.Float | LineNumber: 0 | BytePositionInLine: 14.");
            }
            //但不兼容不带引号的,如: NaN => float.NaN
            try
            {
                str = "{\"Float\":NaN}";
                str.ToObject<Demo6>();
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("'N' is an invalid start of a value. Path: $.Float | LineNumber: 0 | BytePositionInLine: 9.");
            }
        }


        class Demo7
        {
            public EnumState State { get; set; }
        }
        enum EnumState
        {
            Open, Close
        }

        /// <summary>
        /// SystemTextJson 无法 "Close" => EnumState
        /// </summary>
        [Test]
        public void Test7()
        {
            var str = "{\"State\":\"Close\"}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo7>(str);
            d.State.ShouldBe(EnumState.Close);

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo7>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to DotNetCommon.Test.Serialize.DiffNewtonsoftAndSystemTextJson+EnumState. Path: $.State | LineNumber: 0 | BytePositionInLine: 16.");
            }

            //Newtonsoft还忽略大小写
            str = "{\"State\":\"CLOSE\"}";
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo7>(str);
            d.State.ShouldBe(EnumState.Close);
            str = "{\"Bool\":\"open\"}";
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo7>(str);
            d.State.ShouldBe(EnumState.Open);

            //兼容了
            d = "{\"State\":\"CLOSE\"}".ToObject<Demo7>();
            d.State.ShouldBe(EnumState.Close);
            d = "{\"Bool\":\"open\"}".ToObject<Demo7>();
            d.State.ShouldBe(EnumState.Open);
        }

        class Demo8
        {
            public EnumStateFlag State { get; set; }
        }
        enum EnumStateFlag
        {
            Open = 1, Close = 2, Other = 4
        }
        /// <summary>
        /// SystemTextJson 无法 "Close,Other" => EnumState
        /// </summary>
        [Test]
        public void Test8()
        {
            var str = "{\"State\":\"Close, Other\"}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo8>(str);
            d.State.ShouldBe(EnumStateFlag.Close | EnumStateFlag.Other);

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo8>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to DotNetCommon.Test.Serialize.DiffNewtonsoftAndSystemTextJson+EnumStateFlag. Path: $.State | LineNumber: 0 | BytePositionInLine: 23.");
            }

            //Newtonsoft还忽略大小写
            str = "{\"State\":\"CLOSE, other\"}";
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo8>(str);
            d.State.ShouldBe(EnumStateFlag.Close | EnumStateFlag.Other);
            //Newtonsoft还忽略空格
            str = "{\"State\":\"Open,other\"}";
            d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo8>(str);
            d.State.ShouldBe(EnumStateFlag.Open | EnumStateFlag.Other);

            //Newtonsoft也无法将 ["Open","Other"] 转为枚举
            str = "{\"State\":[\"Open\",\"other\"]}";
            try
            {
                d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo8>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldStartWith("Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'DotNetCommon.Test.Serialize.DiffNewtonsoftAndSystemTextJson+EnumStateFlag' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.");
            }

            //兼容了
            d = "{\"State\":\"CLOSE, other\"}".ToObject<Demo8>();
            d.State.ShouldBe(EnumStateFlag.Close | EnumStateFlag.Other);
            d = "{\"State\":\"Open,other\"}".ToObject<Demo8>();
            d.State.ShouldBe(EnumStateFlag.Open | EnumStateFlag.Other);
        }

        class Demo9
        {
            public DateTime Birth { get; set; }
        }

        /// <summary>
        /// SystemTextJson 无法 "2023-09-21 01:02:03" => DateTime
        /// </summary>
        [Test]
        public void Test9()
        {
            var str = "{\"Birth\":\"2023-09-21 01:02:03\"}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo9>(str);
            d.Birth.ShouldBe(DateTime.Parse("2023-09-21 01:02:03"));

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo9>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("The JSON value could not be converted to System.DateTime. Path: $.Birth | LineNumber: 0 | BytePositionInLine: 30.");
            }

            //兼容了
            d = "{\"Birth\":\"2023-09-21 01:02:03\"}".ToObject<Demo9>();
            d.Birth.ShouldBe(DateTime.Parse("2023-09-21 01:02:03"));
        }

        class Demo10
        {
            public string Name { get; set; }
        }

        /// <summary>
        /// System.Text.Json 无法反序列化无引号的属性
        /// </summary>
        [Test]
        public void Test10()
        {
            var str = "{Name:\"tom\"}";
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<Demo10>(str);
            d.Name.ShouldBe("tom");

            try
            {
                System.Text.Json.JsonSerializer.Deserialize<Demo10>(str);
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("'N' is an invalid start of a property name. Expected a '\"'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.");
            }
        }

        /// <summary>
        /// System.Text.Json 无法操作 DataTable
        /// </summary>
        [Test]
        public void Test11()
        {
            var dt = new DataTable("test");
            dt.Columns.Add("id");
            dt.Columns.Add("Name");
            dt.Columns.Add("Score");
            for (int i = 0; i < 2; i++)
            {
                var row = dt.NewRow();
                row["id"] = i + 1;
                row["Name"] = "tom" + i + 1;
                row["Score"] = 100 - i - 1;
                dt.Rows.Add(row);
            }

            var str = Newtonsoft.Json.JsonConvert.SerializeObject(dt, new Newtonsoft.Json.JsonSerializerSettings
            {
                ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
            });

            try
            {
                System.Text.Json.JsonSerializer.Serialize(dt, new System.Text.Json.JsonSerializerOptions
                {
                    ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles,
                });
            }
            catch (Exception ex)
            {
                ex.Message.ShouldContain("Serialization and deserialization of 'System.Type' instances");
            }
        }

        /// <summary>
        /// System.Text.Json 不支持dynamic
        /// </summary>
        [Test]
        public void Test12()
        {
            var str = """
                {
                  "Name":"tom",
                  "Age":20
                }
                """;
            dynamic dy = Newtonsoft.Json.JsonConvert.DeserializeObject(str);
            ((int)dy.Age).ShouldBe(20);
            ((string)dy.Name).ShouldBe("tom");

            var str2 = """
                [{
                  "Name":"tom",
                  "Age":20
                }]
                """;
            dynamic dy3 = Newtonsoft.Json.JsonConvert.DeserializeObject(str2);
            ((string)dy3[0].Name).ShouldBe("tom");
            ((int)dy3[0].Age).ShouldBe(20);

            var jsonObj = System.Text.Json.JsonSerializer.Deserialize<JsonObject>(str);
            dynamic dy2 = jsonObj;
            try
            {
                string name2 = dy2.Name;
            }
            catch (Exception ex)
            {
                ex.Message.ShouldBe("'System.Text.Json.Nodes.JsonObject' does not contain a definition for 'Name'");
            }
        }
    }
}
